package com.qire.manhua.model.entity;

import android.graphics.Rect;

import com.qire.common.basic.DateUtil;
import com.qire.common.collect.ListUtil;
import com.qire.common.model.entity.UserEntity;
import com.qire.common.utils.EquipmentInfoUtil;
import com.qire.common.utils.FileManager;
import com.qire.manhua.model.enumeration.BookStatus;
import com.qire.manhua.model.enumeration.FreeType;
import com.qire.manhua.view.comicReader.renderer.PictureResource;
import com.qire.common.collect.SafeIterableMap;
import com.qire.common.collect.SafeIterableList;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;

/**
 * 漫画书实体：他由2个部分组成，目录及章节内容，
 * 我们并没有强制建立索引，是因为使用场景中可能存在目录单独使用的地方，通过buildIndexing可以建立一个章节和目录的索引。
 * DirectoryInfo 是目录信息
 * ChaptersInfo  是章节信息
 * */
public class ComicBookEntity {

    /** 漫画ID */
    public final int id;
    /** 漫画名 */
    public final String bookName;
    /** 漫画封面横向 */
    private String coverPlotHorizontal;
    /** 漫画封面竖向 */
    private String coverPlotVertical;
    /** 是否收藏漫画 */
    private boolean whetherCollect = false; //true 收藏 false 未收藏
    /** 连载状态 true 完结 false 连载中 */
    private boolean isEndState = false;
    /** 漫画加锁信息 */
    private LockInfo lockInfo;
    /** 漫画折扣信息 */
    private DiscountInfo discountInfo;
    /** 当前正在阅读章节 */
    private ChaptersInfo currentReadingChapters = null;

    // TODO:可以考虑使用MAP替换用章节ID作为KEY
    public final ArrayList<DirectoryInfo> directoryInfoList = new ArrayList<>();
    public final HashMap<String, ChaptersInfo> chaptersInfoMap = new HashMap<>();


    public ComicBookEntity(int id, String bookName) {
        this.id = id;
        this.bookName = bookName;
    }

    public ComicBookEntity(int id, String bookName, int chapterID) {
        this(id,bookName);
        chaptersReading(chapterID);
    }

    public ComicBookEntity(int id, String bookName, int chapterID, String bookCover) {
        this(id,bookName,chapterID);
        setCoverPlotHorizontal(bookCover);
        setCoverPlotVertical(bookCover);
    }

    /**
     * 设置横向封面图片
     * @param coverPlotHorizontal 横向封面图片url
     */
    public void setCoverPlotHorizontal(String coverPlotHorizontal) {
        this.coverPlotHorizontal = coverPlotHorizontal;
    }

    /**
     * 设置纵向封面图片
     * @param coverPlotVertical 纵向封面图片url
     */
    public void setCoverPlotVertical(String coverPlotVertical) {
        this.coverPlotVertical = coverPlotVertical;
    }

    /**
     * @return 横向封面图片url
     */
    public String getCoverPlotHorizontal() {
        return coverPlotHorizontal;
    }

    /**
     * @return 纵向封面图片url
     */
    public String getCoverPlotVertical() {
        return coverPlotVertical;
    }

    /**
     * 更新收藏漫画状态行为
     * @param whetherCollect true收藏，false取消收藏
     */
    public void whetherCollect(boolean whetherCollect) {
        this.whetherCollect = whetherCollect;
    }

    /**
     * @return 漫画收藏状态，true收藏，false取消收藏
     */
    public boolean whetherCollect() {
        return whetherCollect;
    }

    /**
     * 更新连载状态
     * @param isEndState 漫画连载状态：true 已完结 false 连载中
     */
    public void setEndState(boolean isEndState) {
        this.isEndState = isEndState;
    }

    /**
     * @return 漫画连载状态：true 已完结 false 连载中
     */
    public boolean isEndState() {
        return isEndState;
    }

    /**
     * 加载漫画加锁信息
     * @param lockInfo 整本漫画的加锁信息
     */
    public void loadLockInfo(LockInfo lockInfo) {
        this.lockInfo = lockInfo;
    }

    /**
     * @return 整本漫画的加锁信息
     */
    public LockInfo lockInfo() {
        return lockInfo;
    }

    /**
     * 加载折扣信息
     * @param notFreeCount      当前付费章节数
     * @param discountAmount    当前可优惠折扣额度
     */
    public void loadDiscountInfo(int notFreeCount, float discountAmount) {
        this.discountInfo = new DiscountInfo(notFreeCount, discountAmount);
    }

    /**
     * @return 整本漫画的折扣信息
     */
    public DiscountInfo discountInfo() {
        return discountInfo;
    }

    public void clearDirectoryList() {
        directoryInfoList.clear();
    }
    public void addDirectoryInfo(DirectoryInfo directoryInfo) {
        directoryInfoList.add(directoryInfo);
    }
    //本应该在add时进行绑定，但是由于某些情况下，只会使用刀目录，或者单独章节，所以每次都建立会造成没必要的开销
    public boolean buildIndexing(DirectoryInfo directoryInfo, ChaptersInfo chaptersInfo) {
        if(directoryInfo.chapterId != chaptersInfo.id)
            return false;
        if(directoryInfo.chaptersInfo != null)
            return false;

        //TODO:必须重构的，结构有点混乱，在某些地方可能构建了章节与目录的连接，导致无法绑定到最新目录中，应该在创建目录时制定章节。
//        if(chaptersInfo.directoryInfo != null)
//            return false;

//        directoryInfo.chaptersInfo = chaptersInfo;
        chaptersInfo.directoryInfo = directoryInfo;
        return true;
    }

    public ChaptersInfo currentReadingChapters() {
        return currentReadingChapters;
    }

    /**
     * 是否已阅读到首或尾章,false 则不能在翻动，true 表示可以
     * @param direction 方向，true 为下一页，false 为上一页
     * @return {@code direction} 为 true 时，当前页为最后一章节则返回 false，否则返回 true <br />
     * {@code direction} 为 false 时，当前页为第一章节则返回 false，否则返回 true
     */
    public boolean canContinueNext(boolean direction) {
        if(ListUtil.isEmpty(directoryInfoList)) {
            return false;
        }
        int directoryIndex = direction ? directoryInfoList.size() - 1 : 0;
        return directoryInfoList.get(directoryIndex).chapterId != currentReadingChapters.id;
    }

    public int currentReadingChaptersPosition() {
        if(currentReadingChapters == null)
            return 0;
        return getChaptersPositionByChapterId(currentReadingChapters.id);
    }

    public int getChaptersPositionByChapterId(int chapterId) {
        int position = 0;

        if(currentReadingChapters == null)
            return position;
        if(chapterId==0)
            return position;
        for(DirectoryInfo directoryInfo : directoryInfoList) {
            if(directoryInfo.chapterId == chapterId){
                break;
            }
            position++;
        }

        return position;
    }

    /**
     * 1.如果存在正在阅读章节返回该目录
     * 2.如果不存在正在阅读页则返回目录列表第一个
     * 3.如果目录列表位 Empty 则返回null
     * @return 返回默认阅读的章节目录
     */
    public DirectoryInfo defReadingDirectoryInfo() {
        DirectoryInfo directoryInfo = currentReadingDirectoryInfo();
        if(directoryInfo != null) {
            return directoryInfo;
        }
        if(ListUtil.notEmpty(directoryInfoList)) {
            return directoryInfoList.get(0);
        }
        return null;
    }

    /**
     * @return 返回当前正在阅读的章节目录，如果正在阅读不存在则返回 null
     */
    public DirectoryInfo currentReadingDirectoryInfo() {
        if(currentReadingChapters == null || directoryInfoList == null)
            return null;
        if(currentReadingChapters.directoryInfo != null)
            return currentReadingChapters.directoryInfo;
        for(DirectoryInfo directoryInfo : directoryInfoList) {
            if(buildIndexing(directoryInfo,currentReadingChapters)) {
                return directoryInfo;
            }
        }
        return null;
    }

    /**
     * 通过索引位置查询目录
     * @param index 索引位置
     * @return 目录实例
     */
    public DirectoryInfo findDirectoryInfo(int index) {
        return ListUtil.range(directoryInfoList, index) ? directoryInfoList.get(index) : null;
    }

    public ChaptersInfo chaptersPreInstalled(int chaptersId) {
        String key = String.valueOf(chaptersId);
        ChaptersInfo chaptersInfo = chaptersInfoMap.get(key);
        if(chaptersInfo == null) {
            chaptersInfo = new ChaptersInfo(chaptersId);
            chaptersInfoMap.put(key, chaptersInfo);
        }
        return chaptersInfo;
    }

    public ChaptersInfo chaptersReading(int chaptersId) {
        return chaptersReading(chaptersPreInstalled(chaptersId));
    }

    public ChaptersInfo chaptersReading(ChaptersInfo chaptersInfo) {
        currentReadingChapters = chaptersInfo;
        return currentReadingChapters;
    }

    /**
     * 解锁信息
     */
    public class LockInfo {
        public final BookStatus saleState;
        public final String     lockIntroduction;       //: "会员限时免费阅读此本漫画",
        public final float      originalPrice;
        public final float      vipPrice;
        public final String     vipPriceStr;
        public final float      buyPrice;
        public final String     buyPriceStr;
        public final FreeType   bookFreeType;
        public final String     limitFreeTime;

        private boolean needFirstUnlock = false;        //首次进入是否需要解锁，为true不管是否开启自动解锁,都需要手动解锁一次，已作提示

        public LockInfo(BookStatus saleState, String lockIntroduction, float originalPrice, float vipPrice,
                        String vipPriceStr, float buyPrice, String buyPriceStr, FreeType bookFreeType, String limitFreeTime) {
            this.saleState        = saleState;
            this.lockIntroduction = lockIntroduction;
            this.originalPrice    = originalPrice;
            this.vipPrice         = vipPrice;
            this.vipPriceStr      = vipPriceStr;
            this.buyPrice         = buyPrice;
            this.buyPriceStr      = buyPriceStr;
            this.bookFreeType     = bookFreeType;
            this.limitFreeTime    = limitFreeTime;
        }
        public boolean isCopyrightExpired() {
            return saleState == BookStatus.Expired;
        }
        public boolean needFirstUnlock() {
            return needFirstUnlock;
        }
        public void enableFirstUnlock() {
            needFirstUnlock = true;
        }
        public void disableFirstUnlock() {
            needFirstUnlock = false;
        }
    }

    /**
     * 漫画整书折扣信息
     */
    public class DiscountInfo {
        /** 当前付费章节数 */
        public final int notFreeCount;
        /** 当前可优惠折扣额度 */
        public final float discountAmount;
        public DiscountInfo(int notFreeCount, float discountAmount) {
            this.notFreeCount = notFreeCount;
            this.discountAmount = discountAmount;
        }
    }

    /**
     * 漫画书目录信息
     */
    public class DirectoryInfo {

        public static final int BuyState_NONE = 0;
        public static final int BuyState_LOCKED = 1;
        public static final int BuyState_UNLOCKED = 2;

        public final int        serialId;               //排序ID
        public final String     chapterName;            //章节名
        public final int        chapterBuyState;        //购买状态：0-不需要购买，1-未解锁，2-已解锁，不需要购买不等同于免费
        public final int        chapterId;              //章节ID
        public final boolean    isVipFree;              //是否会员限免: true 是:1,false 否:0
        public final boolean    isFree;                 //是否章节免费: true 是:1,false 否:0
        public final String     chapterCover;           //章节封面
        public final String     updateDate;             //更新日期
        public final int        likeCount;              //点赞数
        public final boolean    isLatest;               //是否是最后最新的一章 true 是 false 否

        public final ChaptersInfo chaptersInfo;

        private boolean canUnlockedByAd;                //是否可以通过广告解锁
        private String unlockedExpiredTime;             //非永久解锁到期时间 格式：2020-05-05 08:54:66

        public DirectoryInfo(int serialId, String chapterName, int chapterBuyState, int chapterId, boolean isVipFree, boolean isFree,
                             String chapterCover, String updateDate, int likeCount, boolean canUnlockedByAd, String unlockedExpiredTime, boolean isLatest) {

            this.serialId               = serialId;
            this.chapterName            = chapterName;
            this.chapterBuyState        = chapterBuyState;
            this.chapterId              = chapterId;
            this.isVipFree              = isVipFree;
            this.isFree                 = isFree;
            this.chapterCover           = chapterCover;
            this.updateDate             = updateDate;
            this.likeCount              = likeCount;
            this.canUnlockedByAd        = canUnlockedByAd;
            this.unlockedExpiredTime    = unlockedExpiredTime;
            this.isLatest               = isLatest;

            this.chaptersInfo           = chaptersPreInstalled(chapterId);
            this.chaptersInfo.directoryInfo = this;
//            buildIndexing(directoryInfo,chaptersInfo);

        }

        public ChaptersInfo chaptersInfo() {
            return chaptersInfo;
        }

        public void unlock3Days() {
            this.unlockedExpiredTime = DateUtil.yMdHms.offsetTime(3, Calendar.DATE);
            this.canUnlockedByAd = false;
        }

        public String getUnlockedExpiredTime() {
            return unlockedExpiredTime;
        }

        public boolean canUnlockedByAd() {
            return this.canUnlockedByAd;
        }

        /**
         * 检查章节是否有阅读锁需要解锁：阅读锁指在漫画阅读时章节是否需要解锁；
         * @return 1.如果章节为免费章节则无锁，返回false;<br />
         *         2.如果章节版权以过期则vip免费也不存在了，返回true，需要解锁，在下一步章节解锁处会判定是否购买过，如果购买过则可以观看否则提示版权信息;<br />
         *         3.如果章节为VIP免费章节且用户属于VIP则无锁，返回false;<br />
         *         4.如果章节为购买状态为加锁状态则有锁，返回true反之为false.*/
        public boolean haveLock() {
            if(chapterBuyState == BuyState_NONE)
                return false;
            if(chapterBuyState == BuyState_UNLOCKED)
                return false;
            if (chaptersInfo != null && !chaptersInfo.needUnlock())
                return false;
            if(isFree)
                return false;
            if(lockInfo.isCopyrightExpired())
                return true;
            if(isVipFree && UserEntity.self.isVip())
                return false;
            return chapterBuyState == BuyState_LOCKED;
        }
        /**
         * 检查章节是否有下载锁需要解锁：下载锁指在下载时章节是否需要解锁；
         * @return 1.如果章节为免费章节则无锁，返回false;<br/>
         *         2.如果章节为VIP免费章节且用户属于<font color="#ffff00">会员</font>则无锁，返回false;<br/>
         *         3.如果章节为购买状态为加锁状态则有锁，返回true反之为false.*/
        public boolean haveDownloadLock() {
            if(isFree)
                return false;
            if(isVipFree && UserEntity.self.isVip())
                return false;
            return chapterBuyState == BuyState_LOCKED;
        }
        /***
         * 不需要购买，不包含需要购买但为解锁，或是需要购买已解锁的情况*/
        public boolean notNeedBuy() {
            return chapterBuyState == BuyState_NONE;
        }

        /**
         * 是否可以展示改章节
         * <pre>
         *  检查版权是否过期
         * 1.不需要收费章节可以显示
         * 2.支付购买过的章节可以显示
         * 3.活动免费及需要解锁的不能显示（活动免费：限时免费，VIP免费等官方平台推出的需要解锁但享受平台优惠的章节）
         * </pre>*/
        public boolean canNotShow() {

            //NOTE: 检查版权是否过期
            //NOTE：1.不需要收费章节可以显示
            //NOTE：2.支付购买过的章节可以显示
            //NOTE：3.活动免费及需要解锁的不能显示（活动免费：限时免费，VIP免费等官方平台推出的需要解锁但享受平台优惠的章节）

            if(!lockInfo.isCopyrightExpired())
                return false;

            if(isVipFree)
                return true;
            if(chapterBuyState == DirectoryInfo.BuyState_LOCKED)
                return true;
            return false;
        }
    }

    /**
     * 漫画书章节信息
     */
    public class ChaptersInfo {
        public final int id;
//        public final SafeIterableList<PictureResource> pictureResourcesList = new SafeIterableList<>();
        public final ArrayList<PictureResource> pictureResourcesList = new ArrayList<>();
        /** 本地下载缓存目录，与未下载的缓存临时目录不是一个概念 */
        public final File directory;


        private DirectoryInfo directoryInfo;
        private boolean notLoaded = true;
        private boolean haveRead = false;
        private boolean needUnlock = true;
        private ComicComment[] commentBests;
//        private HashMap<String,CopyOnWriteArrayList<BulletScreenInfo>> bulletScreenInfoMap = new HashMap<>();
        private SafeIterableMap<String, SafeIterableList<BulletScreenInfo>> bulletScreenInfoMap = new SafeIterableMap<>();

        private ChaptersInfo(int id) {
            this.id = id;
            this.directory = FileManager.allotChapterFolder(ComicBookEntity.this.id, id);
        }

        /**
         * 加载章节内容图片资源
         */
        public void loadAllPictureResource(String[] contentPictures, Rect[] pictureRectArray) {

            pictureResourcesList.clear();

            int pictureCount = contentPictures.length;
            int placeholderCount = pictureRectArray.length;

            PictureResource lastPictureResource = null;

            for(int index = 0; index < pictureCount; index++) {
                Rect placeholderRect = index < placeholderCount ? pictureRectArray[index] : null;
                String contentUrl = contentPictures[index];

                PictureResource pictureResource = new PictureResource(checkUrlToLocalPath(contentUrl, directory), EquipmentInfoUtil.getScreenWidth());
                pictureResource.setBitmapRect(placeholderRect);
                pictureResource.setPreviousResource(lastPictureResource);
                pictureResourcesList.add(pictureResource);
                lastPictureResource = pictureResource;
                pictureResource.setSerialNumber(pictureResourcesList.size());
            }
        }

        /**
         * 检查网络Url的文件是否在本地有缓存，如果有则转换为本地文件地址.<br />
         * 方法内会做文件完整性检查，对于受损的 图片文件会做重新下载处理。
         * @param imageUrl 章节图片的网络地址
         * @param directory 章节本地缓存
         * @return 本地存在返回本地文件绝对路径，本地不存在返回URL网络路径
         */
        private String checkUrlToLocalPath(String imageUrl, File directory) {
            String picName = FileManager.parseFileNameBy(imageUrl);
            // 检查目录是否存在，如果目录存在则表示当前章节有下载缓存过，进入下载缓存的离线阅读方案
            if(FileManager.exists(directory)) {
                // 如果下载本地图片的路径，这个事约定好的，下载服务下载时也是向这个地址存放。
                File imageFile = new File(directory, picName);
                // 检查本地目标图片的有效性，1.是否存在 2.是否受损
                if(FileManager.checkBitmapFileIsValid(imageFile)) {
                    // 图片文件没有问题，返回本地绝对路径
                    return imageFile.getAbsolutePath();
                } else {
                    //TODO 所有的章节图片显示都需要使用这个方法转换，固再这里做本地图片有效检查，
                    // 1、这里需要注意如果是下载完整后受损的，可能会导致缓存URL于服务器URL不同，这有可能使服务器编辑了章节内容导致，这可能会到值url没有服务器源而无法下载了。
                }
            }
            // 目录不存在，则走网络缓存阅读方案
            return imageUrl;
        }

        public SafeIterableMap<String,SafeIterableList<BulletScreenInfo>> bulletScreenInfoMap() {
            return bulletScreenInfoMap;
        }

        public void clearBulletScreen() {
            bulletScreenInfoMap.clear();
        }

        public void loadComments(ComicComment[] commentBests) {
            if(commentBests == null)
                return;
            this.commentBests = commentBests;
        }

        public ComicComment[] commentBests() {
            if(commentBests == null)
                return new ComicComment[0];
            return commentBests;
        }

        /**
         * 是否未预加载过：第一次调用为未加载状态，该方法掉用会永远将未预加载状态设置为已加载过
         * @return true 是未加载,false则是加载过了*/
        public boolean notLoaded() {
            boolean tmp = notLoaded;
            notLoaded = false;
            return tmp;
        }
        /**
         * 是否已阅读过：第一次调用为未阅读状态，该方法掉用会永远将未阅读状态设置为已阅读过
         * @return true 是阅读过了,false则是未阅读状态*/
        public boolean haveRead() {
            boolean tmp = haveRead;
            haveRead = true;
            return tmp;
        }

        public void addLock() {
            needUnlock = true;
        }

        public void unlock() {
            needUnlock = false;
        }

        public boolean needUnlock() {
            return needUnlock;
        }

        public DirectoryInfo directoryInfo() {
            return directoryInfo;
        }

    }

}
